"""
Rate limiting middleware with IP-based limiting
"""
import time
import logging
from typing import Dict, Optional
from collections import defaultdict, deque
from fastapi import Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware

logger = logging.getLogger(__name__)


class RateLimiter:
    """
    In-memory rate limiter using sliding window approach
    """
    
    def __init__(self, requests_per_window: int, window_seconds: int):
        self.requests_per_window = requests_per_window
        self.window_seconds = window_seconds
        # Store requests as deque of timestamps for each IP
        self.requests: Dict[str, deque] = defaultdict(lambda: deque())
        
    def is_allowed(self, client_ip: str) -> tuple[bool, dict]:
        """
        Check if request from client_ip is allowed
        Returns (is_allowed, rate_limit_info)
        """
        now = time.time()
        
        # Clean old requests outside the window
        self._cleanup_old_requests(client_ip, now)
        
        # Get current request count
        current_requests = len(self.requests[client_ip])
        
        # Check if under limit
        if current_requests < self.requests_per_window:
            # Add current request
            self.requests[client_ip].append(now)
            remaining = self.requests_per_window - (current_requests + 1)
            
            rate_info = {
                "limit": self.requests_per_window,
                "remaining": remaining,
                "reset_at": now + self.window_seconds,
                "window_seconds": self.window_seconds
            }
            
            return True, rate_info
        else:
            # Rate limit exceeded
            oldest_request = self.requests[client_ip][0]
            reset_at = oldest_request + self.window_seconds
            
            rate_info = {
                "limit": self.requests_per_window,
                "remaining": 0,
                "reset_at": reset_at,
                "window_seconds": self.window_seconds,
                "retry_after": int(reset_at - now + 1)  # Seconds to wait
            }
            
            return False, rate_info
    
    def _cleanup_old_requests(self, client_ip: str, now: float):
        """Remove requests outside the current window"""
        window_start = now - self.window_seconds
        
        # Remove old requests from the left of deque
        while (self.requests[client_ip] and 
               self.requests[client_ip][0] < window_start):
            self.requests[client_ip].popleft()


class RateLimiterMiddleware(BaseHTTPMiddleware):
    """
    Rate limiting middleware
    """
    
    def __init__(
        self, 
        app, 
        enabled: bool = True,
        requests_per_window: int = 100,
        window_seconds: int = 60,
        ip_whitelist: list = None,
        skip_paths: list = None
    ):
        super().__init__(app)
        self.enabled = enabled
        self.ip_whitelist = ip_whitelist or []
        self.skip_paths = skip_paths or ["/docs", "/redoc", "/openapi.json", "/health"]
        
        if self.enabled:
            self.rate_limiter = RateLimiter(requests_per_window, window_seconds)
        else:
            self.rate_limiter = None
    
    async def dispatch(self, request: Request, call_next):
        # Skip if rate limiting is disabled
        if not self.enabled or not self.rate_limiter:
            return await call_next(request)
        
        # Skip certain paths
        if any(request.url.path.startswith(path) for path in self.skip_paths):
            return await call_next(request)
        
        # Get client IP
        client_ip = self._get_client_ip(request)
        
        # Skip if IP is whitelisted
        if client_ip in self.ip_whitelist:
            return await call_next(request)
        
        # Check rate limit
        is_allowed, rate_info = self.rate_limiter.is_allowed(client_ip)
        
        if not is_allowed:
            logger.warning(f"Rate limit exceeded for IP: {client_ip}")
            
            # Return 429 Too Many Requests
            raise HTTPException(
                status_code=429,
                detail={
                    "error": "Rate limit exceeded",
                    "message": f"Too many requests. Try again in {rate_info.get('retry_after', 60)} seconds.",
                    "rate_limit": {
                        "limit": rate_info["limit"],
                        "remaining": rate_info["remaining"],
                        "reset_at": rate_info["reset_at"],
                        "window_seconds": rate_info["window_seconds"],
                        "retry_after": rate_info.get("retry_after", 60)
                    }
                }
            )
        
        # Add rate limit headers to response
        response = await call_next(request)
        
        response.headers["X-RateLimit-Limit"] = str(rate_info["limit"])
        response.headers["X-RateLimit-Remaining"] = str(rate_info["remaining"])
        response.headers["X-RateLimit-Reset"] = str(int(rate_info["reset_at"]))
        response.headers["X-RateLimit-Window"] = str(rate_info["window_seconds"])
        
        return response
    
    def _get_client_ip(self, request: Request) -> str:
        """
        Get client IP address, considering proxy headers
        """
        # Check for forwarded headers (common in load balancers/proxies)
        forwarded_for = request.headers.get("x-forwarded-for")
        if forwarded_for:
            # X-Forwarded-For can contain multiple IPs, use the first one
            return forwarded_for.split(",")[0].strip()
        
        real_ip = request.headers.get("x-real-ip")
        if real_ip:
            return real_ip.strip()
        
        # Fall back to direct client IP
        if request.client and request.client.host:
            return request.client.host
        
        return "unknown"


def rate_limiter_middleware(
    enabled: bool = True,
    requests_per_window: int = 100,
    window_seconds: int = 60,
    ip_whitelist: list = None,
    skip_paths: list = None
):
    """Factory function to create rate limiter middleware"""
    def create_middleware(app):
        return RateLimiterMiddleware(
            app=app,
            enabled=enabled,
            requests_per_window=requests_per_window,
            window_seconds=window_seconds,
            ip_whitelist=ip_whitelist,
            skip_paths=skip_paths
        )
    return create_middleware